import { useBoolean, useToggle, useUpdateEffect } from 'ahooks';
import { Button, message, Radio, Spin } from 'antd';
import React, { forwardRef, useCallback, useImperativeHandle, useRef, useState } from 'react';
import toLower from 'lodash/toLower';
import toUpper from 'lodash/toUpper';
import useCopyToClipboard from '@/hooks/useCopyToClipboard';
import MemoAceEditor from '../JsonAndXmlEditor/MemoAceEditor';
import styles from './styles.less';
import { format, formatJSON, formatXML, isJSON } from '@/utils/utils';
import IconFont from '../IconFont';
import { mapFormatToEditorMode } from '@/pages/ProcessManager/ProcessRun';
import request from '@/utils/request';

function isXML(str) {
  if (typeof str === 'string') {
    if (str.trim().startsWith('<')) {
      return true;
    }
  }
  return false;
}

const mapModeToFormat = {
  json: 'JSON',
  xml: 'XML',
  text: 'NONE',
};

function CodeEditor(
  {
    mode,
    style,
    height = 388,
    data,
    switchable = false,
    onSwitch,
    formatOnSetValue = true,
    placeholder,
    ...rest
  },
  ref
) {
  const editorInstance = useRef(null);
  const [hover, { setFalse: unsetHover, setTrue: setHover }] = useBoolean(false);
  const [focus, { setFalse: unsetFocus, setTrue: setFocus }] = useBoolean(false);
  const [_state, copyToClipboard] = useCopyToClipboard();
  const [expanded, { toggle }] = useToggle(false);

  const [showPlaceHolder, setShowPlaceHolder] = useState(!data);
  const [loading, setLoading] = useState(false);

  if (!mode) {
    if (isJSON(data)) {
      mode = 'json';
    } else if (isXML(data)) {
      mode = 'xml';
    } else if (mode) {
      mode = 'text';
    } else {
      mode = 'json';
    }
  } else {
    mode = mapFormatToEditorMode[mode] || mode;
  }

  data = data || undefined;
  mode = toLower(mode);

  const [_mode, setMode] = useState(mode);

  useUpdateEffect(() => {
    if (mode !== _mode && mode) {
      setMode(mode);
    }
  }, [mode]);

  const setValue = value => {
    editorInstance.current.setValue(formatOnSetValue ? format(value) : value);
  };

  useImperativeHandle(ref, () => ({
    getValue: () => editorInstance.current.getValue(),
    setValue,
    getFormat: () => mapModeToFormat[_mode],
    hasError: () => {
      const session = editorInstance.current.getSession();
      return session && session.$annotations.length > 0;
    },
    setMode: mode => {
      setMode(mapFormatToEditorMode[mode] || mode);
    },
  }));

  if (formatOnSetValue) {
    if (_mode === 'json') {
      data = formatJSON(data);
    } else if (_mode === 'xml') {
      data = formatXML(data);
    }
  }

  const handleCopy = () => {
    copyToClipboard(editorInstance.current.getValue());
    message.success('复制成功');
  };

  const handleFormat = () => {
    const value = editorInstance.current.getValue();
    if (_mode === 'json') {
      editorInstance.current.setValue(formatJSON(value));
    } else if (_mode === 'xml') {
      editorInstance.current.setValue(formatXML(value));
    }
  };

  const handleRemoveRef = () => {
    if (_mode === 'json') {
      setLoading(true);
      request
        .post('/codeEditor/removeRef', {
          data: {
            input: editorInstance.current.getValue(),
          },
        })
        .then(({ output }) => setValue(output))
        .finally(() => setLoading(false));
    }
  };

  if (!placeholder) {
    if (_mode === 'json') {
      placeholder = '请输入json...';
    } else if (_mode === 'xml') {
      placeholder = '请输入xml...';
    } else if (_mode === 'text') {
      placeholder = '请输入文本...';
    }
  }

  return (
    <>
      {switchable && (
        <Radio.Group
          buttonStyle="solid"
          className="margin-bottom"
          value={_mode}
          onChange={e => {
            setMode(e.target.value);
            onSwitch && onSwitch(e.target.value);
          }}
        >
          <Radio.Button value="json">JSON</Radio.Button>
          <Radio.Button value="xml">XML</Radio.Button>
          <Radio.Button value="text">普通文本</Radio.Button>
        </Radio.Group>
      )}
      <div
        className={styles.wrapper}
        style={{
          ...style,
          border: '1px solid #dcdcdc',
          borderRadius: 2,
          // borderColor: hover || focus ? '#40a9ff' : '#d9d9d9',
        }}
        onMouseEnter={setHover}
        onMouseLeave={unsetHover}
        onFocus={setFocus}
        onBlur={unsetFocus}
      >
        {showPlaceHolder && !rest.readOnly && (
          <span className={styles.placeholder}>{placeholder}</span>
        )}
        <Button
          shape="circle"
          icon={expanded ? 'double-right' : 'tool'}
          style={{
            display: hover ? 'flex' : 'none',
            justifyContent: 'center',
            alignItems: 'center',
          }}
          onClick={toggle}
          className={styles.tool_expand_btn}
        />
        <Button
          style={{ display: expanded && hover ? '' : 'none' }}
          icon="copy"
          type="dashed"
          onClick={handleCopy}
          className={styles.copy_btn}
        >
          复制
        </Button>
        <Button
          style={{ display: expanded && hover ? '' : 'none' }}
          type="dashed"
          onClick={handleFormat}
          className={styles.format_btn}
        >
          <IconFont type="icon-code" />
          格式化
        </Button>

        <Button
          style={{ display: expanded && hover ? '' : 'none' }}
          type="dashed"
          icon="bulb"
          onClick={handleRemoveRef}
          className={styles.remove_ref_btn}
        >
          去除$ref
        </Button>
        <Spin spinning={loading}>
          <MemoAceEditor
            onChange={({ text }) => setShowPlaceHolder(!text)}
            getInstance={instance => {
              editorInstance.current = instance;
            }}
            data={data}
            mode={_mode}
            style={{ height: height - 2, borderRadius: 2 }}
            {...rest}
          />
        </Spin>
      </div>
    </>
  );
}

export default forwardRef(CodeEditor);
